package com.beemans.common.utils

import android.Manifest.permission
import android.annotation.SuppressLint
import android.content.Context
import android.location.*
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.annotation.RequiresPermission
import com.beemans.common.R
import com.beemans.common.ext.toString2
import com.blankj.utilcode.util.NetworkUtils
import com.tiamosu.fly.utils.launchIO
import com.tiamosu.fly.utils.launchMain
import com.tiamosu.fly.utils.locationManager
import java.util.*

/**
 * @author tiamosu
 * @date 2021/8/11.
 */
object LocationUtils {

    /**
     * @return 获取位置信息
     */
    @SuppressLint("ResourceType")
    @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION])
    fun startLocation(
        context: Context,
        locationConfig: LocationConfig.() -> Unit = {},
        callback: LocationCallback.() -> Unit = {}
    ) {
        val locationCallback = LocationCallback().apply(callback)

        fun onLocationFailed(error: String? = null) {
            launchMain {
                val msg = when {
                    error?.isNotBlank() == true -> error
                    else -> R.string.common_location_failed.toString2(context)
                }
                locationCallback.onLocationFailed?.invoke(msg)
            }
        }

        if (!NetworkUtils.isConnected()) {
            val msg = R.string.common_net_connect_failed.toString2(context)
            onLocationFailed(msg)
            return
        }
        if (!TelephoneUtils.isGPSEnabled()) {
            val msg = R.string.common_turn_on_location_service.toString2(context)
            onLocationFailed(msg)
            return
        }

        val config = LocationConfig().apply(locationConfig)
        fun updateLocation(location: Location) {
            kotlin.runCatching {
                val geocoder = Geocoder(context, config.locale)
                val longitude = location.longitude
                val latitude = location.latitude
                val addressList = geocoder.getFromLocation(latitude, longitude, 1)
                val address = addressList?.getOrNull(0)
                if (address == null) {
                    onLocationFailed()
                    return
                }
                launchMain {
                    locationCallback.onLocationSuccess?.invoke(location, address)
                }
            }.onFailure {
                onLocationFailed()
            }
        }

        fun start() {
            launchMain {
                locationCallback.onLocationStart?.invoke()
            }

            val criteria = Criteria().apply {
                //位置解析的精度。Criteria.Accuracy_Fine 精确模式，Criteria.Accuracy_Coarse 模糊模式。
                accuracy = Criteria.ACCURACY_FINE
                //是否提供海拔高度信息
                isAltitudeRequired = false
                //是否提供方向信息
                isBearingRequired = false
                //是否允许运行商计费。
                isCostAllowed = true
                //电池消耗，无、低、中、高，参数 Criteria. NO_REQUIREMENT, Criteria. POWER_LOW, Criteria.POWER_MEDIUM, or Criteria. POWER_HIGH
                powerRequirement = Criteria.POWER_LOW
                //是否提供速度信息
                isSpeedRequired = false
            }.apply(config.criteriaOption)

            val provider = try {
                //enabledOnly：true，代表从打开的设备中查找
                locationManager?.getBestProvider(criteria, true) ?: config.defaultProvider
            } catch (e: Exception) {
                config.defaultProvider
            }
            val location = locationManager?.getLastKnownLocation(provider)
            if (location != null) {
                updateLocation(location)
                return
            }

            fun requestLocationUpdates() {
                val handler = Handler(Looper.getMainLooper())
                val locationListener = object : LocationListener {
                    @SuppressLint("MissingPermission")
                    override fun onLocationChanged(location: Location) {
                        handler.removeCallbacksAndMessages(null)
                        locationManager?.removeUpdates(this)
                        updateLocation(location)
                    }

                    override fun onProviderEnabled(provider: String) {
                    }

                    override fun onProviderDisabled(provider: String) {
                    }

                    @Deprecated("Deprecated in Java")
                    override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
                    }
                }
                //设置间隔时间获取一次定位信息，防止室内GPS定位失效，此处用LocationManager.NETWORK_PROVIDER
                locationManager?.requestLocationUpdates(
                    LocationManager.NETWORK_PROVIDER, 0, 0f, locationListener
                )
                handler.postDelayed({
                    locationManager?.removeUpdates(locationListener)
                    onLocationFailed()
                }, config.timeout)
            }

            launchMain { requestLocationUpdates() }
        }

        launchIO { start() }
    }
}

class LocationConfig(
    var timeout: Long = 5000,
    val criteriaOption: Criteria.() -> Unit = {},
    var defaultProvider: String = LocationManager.NETWORK_PROVIDER,
    var locale: Locale = Locale.getDefault(),
)

class LocationCallback {
    var onLocationStart: (() -> Unit)? = null
    var onLocationSuccess: ((location: Location, address: Address) -> Unit)? = null
    var onLocationFailed: ((error: String) -> Unit)? = null

    /**
     * 开始定位获取位置信息
     */
    fun onLocationStart(onLocationStart: () -> Unit) {
        this.onLocationStart = onLocationStart
    }

    /**
     * 获取位置信息成功
     */
    fun onLocationSuccess(onLocationSuccess: (location: Location, address: Address) -> Unit) {
        this.onLocationSuccess = onLocationSuccess
    }

    /**
     * 获取位置信息失败
     */
    fun onLocationFailed(onLocationFailed: (error: String) -> Unit) {
        this.onLocationFailed = onLocationFailed
    }
}